package informationplatform
import informationplatform.product.*
import informationplatform.milestone.*
import java.text.SimpleDateFormat
import grails.converters.JSON

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.usermodel.HSSFHyperlink
import org.apache.poi.ss.util.CellRangeAddress
import org.apache.poi.ss.util.Region

import informationplatform.overtime.Audited;
import informationplatform.task.Task;

class RouteSummaryController {

	def smf = new SimpleDateFormat("yyyy-MM-dd")

	//记录我的路标过滤条件
	void setRouteViewType(viewTypes){
		session.mrcreate = ""
		session.mrcharge = ""
		session.mrattent = ""
		session.mrinaudit = ""
		session.mrinpreview = ""
		if(viewTypes.contains("myCreate")){
			session.mrcreate = "checked"
		}
		if(viewTypes.contains("myCharge")){
			session.mrcharge = "checked"
		}
		if(viewTypes.contains("myAttent")){
			session.mrattent = "checked"
		}
		if(viewTypes.contains("inAudit")){
			session.mrinaudit = "checked"
		}
		if(viewTypes.contains("inReview")){
			session.mrinpreview = "checked"
		}
	}
	//记录我的功能项过滤条件
	void setFunctionViewType(viewTypes){
		session.mfcreate = ""
		session.mfcharge = ""
		session.mfinvolve = ""
		if(viewTypes.contains("myCreate")){
			session.mfcreate = "myCreate"
		}
		if(viewTypes.contains("myCharge")){
			session.mfcharge = "myCharge"
		}
		if(viewTypes.contains("myInvolve")){
			session.mfinvolve = "myInvolve"
		}
	}

	def myRoute(){
		def viewTypes=[
			"myCreate",
			"myCharge",
			"myAttent",
			"inAudit",
			"inReview"
		]

		if(session.mrcreate == null) //初始化状态
			setRouteViewType(viewTypes);
	}
	def myFunction(){
		def viewTypes=[
			"myCreate",
			"myCharge",
			"myInvolve"
		]
		if(session.mfcreate == null) //初始化状态
			setFunctionViewType(viewTypes);
	}

	def ajaxProductData(){
		def res = []
		def relevantProducts  =  Product.withCriteria{
			or{
				eq('isPrivate',false)
				isNull('isPrivate')
			}
		}
		def relevantProducts1  = ProductTeamMember.withCriteria {
			projections{ distinct("product") }
			member{
				idEq(session.user.id)
			}
			product{ eq('isPrivate',true) }
		}
		relevantProducts.addAll(relevantProducts1)

		//   def defaultProduct = UserProfile.findByUser(session.user)?.defaultProduct
		def defaultProduct
		def dProduct = UserProfile.findByUser(session.user)?.defaultProduct
		if(dProduct && relevantProducts?.indexOf(dProduct)>-1){
			defaultProduct = dProduct
		}

		if(!session?.currProduct ){
			if(defaultProduct){
				session.currProduct = defaultProduct
			}else{
				if(relevantProducts.size()>0){
					session.currProduct = relevantProducts[0]
					def userPro = UserProfile.findByUser(session.user)
					userPro.defaultProduct =  session.currProduct
					userPro.save()
				}
			}
		}
		relevantProducts.each{prod->
			res << [id: prod.id, name: prod.name,
						description: prod.description, creator: prod.creator.realName,
						isPrivate: prod.isPrivate
					]
		}
		render(contentType: "application/json"){ [resData: res] }
	}





	def ajaxRouteData(){
		def res = []

		def product = Product.get(params.prodId.toLong())
		session.currProduct = product
		def userPro = UserProfile.findByUser(session.user)
		userPro.defaultProduct =  product
		userPro.save()

		def viewTypes = []
		if(params.type){viewTypes = params.type.split(",")}

		def filed = params."sort[0][field]"
		def dir = params."sort[0][dir]"

		def attents = []
		if(viewTypes.contains("myAttent")){
			attents = AttentRoute.findAllByUser(session.user)*.route.id
		}

		setRouteViewType(viewTypes);

		def routes = []
		if(viewTypes.size() >0)
		{
			routes = ProductGuidepost.withCriteria{
				eq("product", product)
				or{
					if(viewTypes.contains("myCreate")){
						eq("creator", session.user)
					}
					if(viewTypes.contains("myCharge")){
						eq("director", session.user)
					}

					if(attents.size() > 0){
						inList("id", attents)
					}

					//待审核
					if(viewTypes.contains("inAudit")){
						eq("status", GuidepostStatus.SUBMIT_AUDIT)
					}

					//评审中
					if(viewTypes.contains("inReview")){
						eq("status", GuidepostStatus.SUBMIT_REVIEW)
					}
				}
				if(params.q){
					or{
						ilike("name", "%"+ params.q +"%")
						ilike("description", "%"+ params.q +"%")
					}
				}
				if(filed && dir){
					order(filed, dir)
				} else {
					order("id", "asc")
				}
				if(params.skip != null && params.pageSize != null){
					firstResult params.skip?.toInteger() ?: 0
					maxResults params.pageSize?.toInteger() ?: 100
				}
			}
		}

		def total = 0

		if(viewTypes.size() > 0 ){
			total = ProductGuidepost.withCriteria{
				projections{ count("id")}
				eq("product", product)
				or{
					if(viewTypes.contains("myCreate")){
						eq("creator", session.user)
					}
					if(viewTypes.contains("myCharge")){
						eq("director", session.user)
					}

					if(viewTypes.contains("myAttent") && attents.size() > 0){
						inList("id", attents)
					}
				}
				if(params.q){
					or{
						ilike("name", "%"+ params.q +"%")
						ilike("description", "%"+ params.q +"%")
					}
				}
			}[0]
		}

		res = []
		routes.each{rt->
			def isAttent = attents.contains(rt.id)

			res<< [id: rt.id, name: rt.name, description: rt.description,
						creator: rt.creator.realName, director: rt.director.realName,
						status: rt.status.label, isLock: rt.isLock,
						lastUpdatedBy: rt.audited.lastUpdatedBy,
						lastUpdatedDate: rt.audited.lastUpdatedDate ? smf.format(rt.audited.lastUpdatedDate) : " ",
						createdDate: rt.audited.createdDate ? smf.format(rt.audited.createdDate) : " ",
						isAttent: isAttent
					]
		}

		println "total: $total res : " + res
		render(contentType: "application/json"){
			[resData: res, total: total]
		}
	}

	def ajaxRouteCount(){
		def createTotal = 0, chargeTotal = 0
		createTotal = ProductGuidepost.withCriteria{
			projections{ count("id")}
			eq("creator", session.user)
		}[0]
		chargeTotal = ProductGuidepost.withCriteria{
			projections{ count("id")}
			eq("director", session.user)
		}[0]
		render(contentType: "text/json"){
			[createTotal: createTotal,chargeTotal:chargeTotal]
		}
	}

	def ajaxFuncData(){
		def res = []

		def viewTypes = []
		if(params.type){viewTypes = params.type.split(",")}
		setFunctionViewType(viewTypes);

		def group = session.user.group
		def chargeFunc = MilestoneFunctionPartake.withCriteria{
			//eq("groupOfPartake", group)
			eq("leaderOfpartake", session.user)
			eq("isMain", true)//负责
		}*.milestoneFunction*.id

		def partFunc = MilestoneFunctionPartake.withCriteria{
			eq("groupOfPartake", group)
			eq("isMain", false)//参与
		}*.milestoneFunction*.id
		println "---------------------------------------- chargeFunc: " + chargeFunc
		println "---------------------------------------- partFunc: " + partFunc
		def filed = params."sort[0][field]"
		def dir = params."sort[0][dir]"

		def product = Product.get(params.prodId.toLong())
		def functions = []
		if(viewTypes.size() > 0){
			functions = MilestoneFunction.withCriteria{
				eq("product", product)
				or{
					if(viewTypes.contains("myCreate")){
						eq("creator", session.user)
					}
					if(viewTypes.contains("myCharge") && chargeFunc.size() > 0){
						inList("id", chargeFunc)
					}
					if(viewTypes.contains("myInvolve") && partFunc.size() > 0){
						inList("id", partFunc)
					}
				}
				if(params.q){
					or{
						ilike("name", "%"+ params.q +"%")
						ilike("description", "%"+ params.q +"%")
					}
				}

				if(filed && dir){
					order(filed, dir)
				} else {
					order("id", "asc")
				}

				if(params.skip != null && params.pageSize != null){
					firstResult params.skip?.toInteger() ?: 0
					maxResults params.pageSize?.toInteger() ?: 100
				}
			}
		}

		def total = 0
		if(viewTypes.size() > 0){
			total = MilestoneFunction.withCriteria{

				projections{ count("id")}
				eq("product", product)
				or{
					if(viewTypes.contains("myCreate")){
						eq("creator", session.user)
					}
					if(viewTypes.contains("myCharge") && chargeFunc.size() > 0){
						inList("id", chargeFunc)
					}
					if(viewTypes.contains("myInvolve") && partFunc.size() > 0){
						inList("id", partFunc)
					}
				}

				if(params.q){
					or{
						ilike("name", "%"+ params.q +"%")
						ilike("description", "%"+ params.q +"%")
					}
				}
			}[0]
		}
		functions.each{func->
			res << [id: func.id, name: func.name, description: func.description,
						guidepost: func.guidepost?.name ?: "",
						pversion: func.pversion?.name ?: "",
						milestone: func.milestone?.name ?: "",
						type: func.type, bigType: func.bigType, requirementOrigin: func.requirementOrigin,
						requirementPromiseCompleteDate: func.requirementPromiseCompleteDate ? smf.format(func.requirementPromiseCompleteDate) : " ",
						needSupportRequirementDoc: func.needSupportRequirementDoc,
						needCheckCode: func.needCheckCode,
						supportDesignDocType: func.supportDesignDocType,
						needReviewDesignDoc: func.needReviewDesignDoc,
						needSupportPrototype: func.needSupportPrototype,
						needUnitTesting: func.needUnitTesting,
						startDate: func.startDate ? smf.format(func.startDate) : " ",
						planCompleteDate: func.planCompleteDate ? smf.format(func.planCompleteDate) : " ",
						actualCompleteDate: func.actualCompleteDate ? smf.format(func.actualCompleteDate) : " ",
						status: func.status?.label,
						creator: func.creator.realName
					]
		}

		render(contentType: "application/json"){
			[resData: res, total:total]
		}
	}
	def ajaxFuncPlanData(){
		def res = []
		def id = 0
		res << [id: ++id,name: "开发计划---研发管理系统-路标管理子系统", description:"", creator: "", director: ""]
		render(contentType: "application/json"){
			[resData: res, total: res.size()]
		}
	}

	def ajaxProductSelectData(){
		def res = []
		def products  =  Product.withCriteria{ order("name", "asc") }
		//def defaultProduct = UserProfile.findByUser(session.user)?.defaultProduct

		def relevantProducts  =  Product.withCriteria{
			or{
				eq('isPrivate',false)
				isNull('isPrivate')
			}
		}
		def relevantProducts1  = ProductTeamMember.withCriteria {
			projections{ distinct("product") }
			member{
				idEq(session.user.id)
			}
			product{ eq('isPrivate',true) }
		}
		relevantProducts.addAll(relevantProducts1)

		def defaultProduct
		def dProduct = UserProfile.findByUser(session.user)?.defaultProduct
		if(dProduct && relevantProducts?.indexOf(dProduct)>-1){
			defaultProduct = dProduct
		}

		if(!session?.currProduct ){
			if(defaultProduct){
				session.currProduct = defaultProduct
			}else{
				if(relevantProducts.size()>0){
					session.currProduct = relevantProducts[0]
					def userPro = UserProfile.findByUser(session.user)
					userPro.defaultProduct =  session.currProduct
					userPro.save()
				}
			}
		}
		products.each{prod->
			res << [id: prod.id, name: prod.name,
						description: prod.description, creator: prod.creator.realName,
						isPrivate: prod.isPrivate
					]
		}
		render(contentType: "application/json"){ [resData: res] }
	}

	def ajaxVersionSelectData(){
		def res = []
		def route = null
		if(params.routeId){
			route= ProductGuidepost.get(params.routeId.toLong())
		}

		def versions = ProductVersion.withCriteria{
			eq("guidepost", route)
			order("id", "asc")
		}
		versions.each{ver->
			res << [id: ver.id, name: ver.name, creator: ver.creator.realName,
						director: ver.director.realName,
						functionDesc: ver.functionDesc,//版本功能描述
						scopeDesc: ver.scopeOfApplicationDesc, //版本适用范围
						planFinishDate: ver.planCompleteDate ? smf.format(ver.planCompleteDate) : " ",//
						actualFinishDate: ver.actualCompleteDate ? smf.format(ver.actualCompleteDate) : " ",//实际完成时间
						testDate: ver.pilotscaleExperimentDate ? smf.format( ver.pilotscaleExperimentDate ) : " ",//提交中试时间
						realeseDate: ver.realeseDate ? smf.format(ver.realeseDate) : " ",//释放时间\，
						direction: ver.direction,//版本去向  下版本描述
						status: ver.status.label,
						isLock: ver.isLock
					]
		}
		render(contentType: "application/json"){
			[resData: res, total: res.size()]
		}
	}
	def ajaxStoneData(){
		def res = []
		def id = 0
		def viewTypes = []
		if(params.type){viewTypes = params.type.split(",")}

		def filed = params."sort[0][field]"
		def dir = params."sort[0][dir]"

		def ver = ProductVersion.get(params.versionId.toLong())
		def stones = Milestone.withCriteria{
			eq("pversion", ver)
			if(filed && dir){
				order(filed, dir)
			} else{
				order("no", "asc")
			}
		}

		stones.each{st->
			res << [id: st.id, name: st.name, description: st.description,
						no: st.no,
						startDate: smf.format(st.startDate),
						endDate: smf.format(st.endDate),
						planReviewCompleteDate: st.planReviewCompleteDate,
						actualReviewCompleteDate: st.actualReviewCompleteDate,
						reviewCnt: st.reviewCnt,
						planTestplanReviewCompleteDate: st.planTestplanReviewCompleteDate,
						actualTestplanReviewCompleteDate: st.actualTestplanReviewCompleteDate,
						planDevelopCompleteDate: st.planDevelopCompleteDate,
						actualDevelopCompleteDate: st.actualDevelopCompleteDate,
						planTestCompleteDate: st.planTestCompleteDate,
						actualTestCompleteDate: st.actualTestCompleteDate,
						planSystemTestCompleteDate:st.planSystemTestCompleteDate,
						actualSystemTestCompleteDate: st.actualSystemTestCompleteDate,
						status: st.status
					]

		}
		render(contentType: "application/json"){
			[resData: res, total: res.size()]
		}
	}

	def ajaxAttentsData(){
		def res = []
		def id = 0

		res << [id: ++id,name: "里程碑--研发管理系统-路标管理子系统", description:"", creator: "", director: ""]
		res << [id: ++id,name: "里程碑--研发管理系统-评审管理子系统"]
		res << [id: ++id,name: "里程碑--公共研发部信息管理平台"]
		res << [id: ++id,name: "里程碑--研发管理系统-任务管理子系统"]
		res << [id: ++id,name: "里程碑--研发管理系统-路标管理子系统"]
		res << [id: ++id,name: "里程碑--研发管理系统-评审管理子系统"]
		res << [id: ++id,name: "里程碑--公共研发部信息管理平台"]

		render(contentType: "application/json"){
			[resData: res, total: res.size()]
		}
	}


	def routeExport(){

	}

	def ajaxRouteSelectData(){
		def res = []

		def product = Product.get(params.prodId.toLong())


		def routes = ProductGuidepost.withCriteria{
			eq("product", product)
			order("id", "asc")

		}

		res = []
		routes.each{rt->
			res<< [id: rt.id, name: rt.name, description: rt.description,
						creator: rt.creator.realName, director: rt.director.realName,
						status: rt.status.label, isLock: rt.isLock,
						lastUpdatedBy: rt.audited.lastUpdatedBy,
						lastUpdatedDate: rt.audited.lastUpdatedDate ? smf.format(rt.audited.lastUpdatedDate) : " ",
						createdDate: rt.audited.createdDate ? smf.format(rt.audited.createdDate) : " "
					]
		}

		render(contentType: "application/json"){
			[resData: res, total: res.size()]
		}
	}

	//设置单元格格式
	void setCellStyle(CellStyle style, Font font, short align, short vAlign, short background = -1){
		style.setFont(font)
		style.setWrapText(true)
		style.setAlignment(align); // 左右居中
		style.setVerticalAlignment(vAlign); // 上下居中
		if(background != -1){
			style.setFillForegroundColor(background)
			style.setFillPattern(CellStyle.SOLID_FOREGROUND)
		}
		style.setBorderBottom(CellStyle.BORDER_THIN)
		style.setBottomBorderColor(IndexedColors.BLACK.getIndex())
		style.setBorderLeft(CellStyle.BORDER_THIN)
		style.setLeftBorderColor(IndexedColors.BLACK.getIndex())
		style.setBorderRight(CellStyle.BORDER_THIN)
		style.setRightBorderColor(IndexedColors.BLACK.getIndex())
		style.setBorderTop(CellStyle.BORDER_THIN)
		style.setTopBorderColor(IndexedColors.BLACK.getIndex())
	}

	String exportToFile(params, String path) {
		println " ------exportToFile " + params
		FileOutputStream fileOut = null
		File filePath = new File(path)
		if (!filePath.exists()) {
			filePath.mkdirs()
		}
		def product = Product.get(params.prodId.toLong())
		def version = ProductVersion.get(params.versionId.toLong())
		def timeString = (new Date()).getTime()
		def fileName = '/'+ product.name+'_'+ version.guidepost.name+ "_" + version.name + '_' + timeString + '.xls'
		def sheet1Name = "版本计划表", sheet2Name="版本功能范围", sheet3Name="里程碑", sheet4Name="功能项开发计划"

		try {
			Workbook wb = new HSSFWorkbook()
			fileOut = new FileOutputStream(path + fileName)
			Sheet sheet1 = wb.createSheet(sheet1Name)
			Sheet sheet2 = wb.createSheet(sheet2Name)
			Sheet sheet3 = wb.createSheet(sheet3Name)
			Sheet sheet4 = wb.createSheet(sheet4Name)
			// Sheet sheet4 = wb.createSheet(sheet4Name)
			CellStyle firstLineStyle = wb.createCellStyle()
			Font font = wb.createFont()
			font.setFontHeightInPoints((short)12)
			font.setBoldweight(Font.BOLDWEIGHT_BOLD)
			setCellStyle(firstLineStyle, font, CellStyle.ALIGN_LEFT, CellStyle.VERTICAL_CENTER,IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex())

			CellStyle headStyle = wb.createCellStyle()
			Font font1 = wb.createFont()
			font1.setFontHeightInPoints((short)11)
			font1.setBoldweight(Font.BOLDWEIGHT_BOLD)
			setCellStyle(headStyle, font1, CellStyle.ALIGN_CENTER, CellStyle.VERTICAL_CENTER,IndexedColors.LIGHT_YELLOW.getIndex())

			CellStyle dataStyle = wb.createCellStyle()
			Font font2 = wb.createFont()
			font2.setFontHeightInPoints((short)10)
			font2.setBoldweight(Font.BOLDWEIGHT_NORMAL)
			setCellStyle(dataStyle, font2, CellStyle.ALIGN_LEFT, CellStyle.VERTICAL_CENTER)

			CellStyle redBoldStyle = wb.createCellStyle()
			Font font3 = wb.createFont()
			font3.setFontHeightInPoints((short)10)
			font3.setBoldweight(Font.BOLDWEIGHT_BOLD)
			font3.setColor(Font.COLOR_RED)
			setCellStyle(redBoldStyle, font3, CellStyle.ALIGN_CENTER, CellStyle.VERTICAL_CENTER)

			//---------------------------------------------------------------
			//创建超链接样式
			//Author: LJ, UpateDate:2013-11-7
			//---------------------------------------------------------------
			def linkStyle = wb.createCellStyle()
			def linkFont = wb.createFont()
			linkFont.setFontHeightInPoints((short)10)
			linkFont.setBoldweight(Font.BOLDWEIGHT_NORMAL)
			linkFont.setUnderline((byte)1)
			linkFont.setColor(IndexedColors.RED.getIndex())
			setCellStyle(linkStyle, linkFont, CellStyle.ALIGN_LEFT, CellStyle.VERTICAL_CENTER,IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex())
			//---------------------------------------------------------------END
			
			//----------------"版本计划表"------------------------------
			short rowNo = 0;
			Row row = sheet1.createRow(rowNo++)

			row.setHeight((short)15.625 * 30)
			sheet1.addMergedRegion(new CellRangeAddress(0, 0, 0, 3))
			Cell cell = row.createCell(0)
			cell.setCellValue("公共研发部网管版本发布计划表")
			row.createCell(1)
			row.createCell(2)
			row.createCell(3)

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(firstLineStyle)
			}
			row = sheet1.createRow(rowNo++)
			sheet1.addMergedRegion(new CellRangeAddress(1,1, 0, 0))
			cell = row.createCell(0)
			cell.setCellValue("产品名称")

			sheet1.addMergedRegion(new CellRangeAddress(1, 1, 1, 1))
			cell = row.createCell(1)
			cell.setCellValue("产品版本")

			sheet1.addMergedRegion(new CellRangeAddress(1, 1, 2, 3))
			cell = row.createCell(2)
			cell.setCellValue("配套软件 /软件名称(版本)")
			cell = row.createCell(3)
                        
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(headStyle)
			}
                        
//			row = sheet1.createRow(rowNo++)
//			cell = row.createCell(0)
//			cell = row.createCell(1)
//			cell = row.createCell(2)
//			cell.setCellValue("软件类型")
//			cell = row.createCell(3)
//			cell.setCellValue("软件版本")
                        
//			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
//				row.getCell(colIx).setCellStyle(headStyle)
//			}

			for(int i = 0; i <=3; i++) {
				sheet1.setColumnWidth((short) i, (short) 35.7 * 280)
			}

			row = sheet1.createRow(rowNo++)
			cell = row.createCell(0)
			cell.setCellValue(product.name)
			cell = row.createCell(1)
			cell.setCellValue(version.name)
                        
                        sheet1.addMergedRegion(new CellRangeAddress(2, 2, 2, 3))
			cell = row.createCell(2) 

			
                        def softwares = SupportingSoftware.withCriteria{
                             eq('productVersion', version)
                        }
                        def sftString =""
                        for(def i=0;i< softwares.size();i++){
                            sftString += softwares[i]?.softwareName+"（"  + softwares[i].softwareVersion + "）   "
                        }
			cell.setCellValue(sftString)
                        cell = row.createCell(3)
			//cell.setCellValue("")
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(dataStyle)
			}

			row = sheet1.createRow(rowNo++)
			cell = row.createCell(0)
			cell.setCellValue("开始日期")
			cell = row.createCell(1)
			cell.setCellValue("实际完成日期")
			cell = row.createCell(2)
			cell.setCellValue("提交中试日期")
			cell = row.createCell(3)
			cell.setCellValue("版本释放日期")
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(headStyle)
			}
			row = sheet1.createRow(rowNo++)
			cell = row.createCell(0)
			cell.setCellValue("${version.planCompleteDate ? smf.format(version.planCompleteDate) : ' '}")
			cell = row.createCell(1)
			cell.setCellValue("${version.actualCompleteDate? smf.format(version.actualCompleteDate):' '}")
			cell = row.createCell(2)
			cell.setCellValue("${version.pilotscaleExperimentDate ?smf.format(version.pilotscaleExperimentDate): ' '}")
			cell = row.createCell(3)
			cell.setCellValue("${version.realeseDate ? smf.format(version.realeseDate) : ' '}")
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(dataStyle)
			}

			row = sheet1.createRow(rowNo++)
			sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 0, 3))
			cell = row.createCell(0)
			cell.setCellValue("版本发布工作计划")
			row.createCell(1)
			row.createCell(2)
			row.createCell(3)
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(headStyle)
			}

			row = sheet1.createRow(rowNo++)
			sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 2, 3))
			cell = row.createCell(0)
			cell.setCellValue("工作阶段")

			cell = row.createCell(1)
			cell.setCellValue("时间计划")

			sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 2, 3))
			cell = row.createCell(2)
			cell.setCellValue("详细说明")
			cell = row.createCell(3)

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(headStyle)
			}

			def stones =  Milestone.withCriteria{
				eq("pversion", version)
				order("no", "asc")
			}

			stones.each{st->
				row = sheet1.createRow(rowNo++)
				cell = row.createCell(0)
				cell.setCellValue(st.name)
				cell = row.createCell(1)
				cell.setCellValue("${st.startDate ? smf.format(st.startDate):''}~${st.endDate ? smf.format(st.endDate) : ''}")

				sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 2, 3))
				cell = row.createCell(2)
				cell.setCellValue(st.description ?:"")
				cell = row.createCell(3)
				cell.setCellValue("")
				for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
					if(colIx == 1)
						row.getCell(colIx).setCellStyle(redBoldStyle)
					else
						row.getCell(colIx).setCellStyle(dataStyle)
				}
			}

			row = sheet1.createRow(rowNo++)
			sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 0, 3))
			cell = row.createCell(0)
			cell.setCellValue("版本描述")
			cell = row.createCell(1)
			cell = row.createCell(2)
			cell = row.createCell(3)
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(headStyle)
			}

			row = sheet1.createRow(rowNo++)
			sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 1, 3))
			cell = row.createCell(0)
			cell.setCellValue("版本功能说明")
			cell = row.createCell(1)
			cell.setCellValue(version.functionDesc ?: "")
			cell = row.createCell(2)
			cell = row.createCell(3)
			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(dataStyle)
			}
			row = sheet1.createRow(rowNo++)

			cell = row.createCell(0)
			cell.setCellValue("版本适用范围")
			cell = row.createCell(1)

			sheet1.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 1, 3))
			cell = row.createCell(1)
			cell.setCellValue(version.scopeOfApplicationDesc ?: "")
			cell = row.createCell(2)
			cell = row.createCell(3)

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(dataStyle)
			}

			//----------------------"版本功能范围"-------------------
			rowNo = 0
			row = sheet2.createRow(rowNo++)
			row.setHeight((short)15.625 * 30)
			sheet2.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 0, 22))
			cell = row.createCell(0)
			cell.setCellValue("网管版本功能范围")
			for(int i =1; i < 23;i++){ row.createCell(i)}

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(firstLineStyle)
			}

			row = sheet2.createRow(rowNo++)
			sheet2.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 1, 22))
			cell = row.createCell(0)
			cell.setCellValue(product.name)
			cell = row.createCell(1)
			cell.setCellValue(version.name)
			for(int i = 2; i < 23;i++){ row.createCell(i)}

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(dataStyle)
			}

			List strHead = [
				"序号",
				"更改类型",
				"功能大类",
				"功能说明",
				"里程碑",
				"需求来源",
				"承诺完成时间",
				"case号",
				"状态",
				"来源版本",
				"负责专项",
				"参与专项",
				"负责人",
				"开发完成时间",
				"评审级别",
				"是否提供文档",
				"是否代码审查",
				"开发完成需提供的文档",
				"文档是否需要评审",
				"是否提供系统原型",
				"是否单元测试",
				"备注",
				"RDM任务"
			]
			row = sheet2.createRow(rowNo++)
			for(int i = 0; i< strHead.size(); i++){
				cell = row.createCell(i)
				cell.setCellValue(strHead[i])
				cell.setCellStyle(headStyle)

				short iWidth = 0
				switch(i){
					case 0 : iWidth = 35.7 * 80; break
					case 3 : iWidth = 35.7 * 280; break
					default: iWidth = 35.7 * 100; break;
				}
				sheet2.setColumnWidth((short) i, iWidth)
			}

			def functions = MilestoneFunction.withCriteria{
                                eq("functionType",MilestoneFunctionType.USER_FUNCTION)
				eq("pversion", version)
				order("startDate", "asc")
			}

			def orVersion = ""
			if(version.origins != null){ //??? 功能项上无此内容
				version.origins.each{ov->
					orVersion += ov.origin.name + ";"
				}
			}
			
			
			def index = 0
			for(MilestoneFunction func : functions){
				def chargerUser = MilestoneFunctionPartake.withCriteria{
					eq("milestoneFunction",func)
					eq("isMain", true)//负责
				}*.leaderOfpartake*.realName

				def chargeGroup = MilestoneFunctionPartake.withCriteria{
					eq("milestoneFunction",func)
					eq("isMain", true)//负责
				}*.groupOfPartake*.name

				def charger = chargerUser.size() > 0 ? chargerUser.join(",") : ""
				def group = chargeGroup.size() > 0 ? chargeGroup.join(",") : ""

				def partFunc = MilestoneFunctionPartake.withCriteria{
					eq("milestoneFunction", func)
					eq("isMain", false)//参与
				}*.leaderOfpartake*.realName

				def parter = partFunc.size() > 0 ? partFunc.join(",") : ""

				def valList = []
				valList << ++index
				valList.add(func.type ?: " ")
				valList.add(func.bigType ?: " ")
                                
                
                                def desc = func.name ?:" "
                                if(func.description){
                                      desc += "【"+func.description +"】" 
                                }
				valList.add(desc)
				valList.add(func.milestone ? func.milestone.name : "\\")
				valList.add(func.requirementOrigin?: " ")
				valList.add(func.requirementPromiseCompleteDate ? smf.format(func.requirementPromiseCompleteDate) : ' ')

				valList.add(func.testCase?: " ")
				valList << func.status.label
				valList << orVersion
				valList << group
				valList << parter
				valList << charger

				valList.add(func.actualCompleteDate ? smf.format(func.actualCompleteDate ) : ' ')
				valList.add(func.reviewLevel ?: "")//"评审内容(不评审/专项内/跨专项)"
				valList.add(func.needSupportRequirementDoc ? "是":"否")
				valList.add(func.needCheckCode  ? "是":"否")
				valList.add(func.supportDesignDocType ?: "")
				valList.add(func.needReviewDesignDoc ? "是":"否")
				valList.add(func.needSupportPrototype ? "是":"否")
				valList.add(func.needUnitTesting ? "是":"否")
				valList << " "
				valList.add(func.rdmTask ?: "")

				row = sheet2.createRow(rowNo++)
				for(int colNo = 0; colNo < valList.size(); colNo++){
					cell = row.createCell(colNo)
					cell.setCellValue(valList[colNo].toString())
					cell.setCellStyle(dataStyle)
					
					//---------------------------------------------------------------
					//Author: LJ, UpateDate:2013-11-7
					//---------------------------------------------------------------
					//显示需求来源和其超链接
					if(colNo==5 && func.requirementOriginLink){
						def requireLink = new HSSFHyperlink(HSSFHyperlink.LINK_URL)
						requireLink.setLabel(func.requirementOrigin)
						requireLink.setAddress(func.requirementOriginLink)
						cell.setHyperlink(requireLink)
						cell.setCellStyle(linkStyle)
					}
					
					//显示case 号及其超链接
					if(colNo==7 && func.testCaseLink){
						def testLink = new HSSFHyperlink(HSSFHyperlink.LINK_URL)
						testLink.setLabel(func.testCase)
						testLink.setAddress(func.testCaseLink)
						cell.setHyperlink(testLink)
						cell.setCellStyle(linkStyle)
					}
					
					//显示case 号及其超链接
					if(colNo==22 && func.rdmTask){
						def testLink = new HSSFHyperlink(HSSFHyperlink.LINK_URL)
						testLink.setLabel(func.rdmTask)
						testLink.setAddress(func.rdmTaskLink)
						cell.setHyperlink(testLink)
						cell.setCellStyle(linkStyle)
					}
					//---------------------------------------------------------------END
					
				}
			}
			
			//---------------------------里程碑-------------------------
			//sheet3
			rowNo = 0
			row = sheet3.createRow(rowNo++)
			row.setHeight((short)15.625 * 30)
			sheet3.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 0, 4))
			cell = row.createCell(0)
			cell.setCellValue("版本里程碑")
			row.createCell(1)
			row.createCell(2)
			row.createCell(3)
			row.createCell(4)

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(firstLineStyle)
			}

			strHead = [
				"里程碑名称",
				"内容",
				"截止日期",
				"详细说明",
				"备注"
			]
			row = sheet3.createRow(rowNo++)
			for(int i = 0; i< strHead.size(); i++){
				cell = row.createCell(i)
				cell.setCellValue(strHead[i])
				cell.setCellStyle(headStyle)

				sheet3.setColumnWidth((short) i, (short) 35.7 * 200)
			}

			index = 0
			stones.each{st->
				row = sheet3.createRow(rowNo++)
				sheet3.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1+4, 0, 0))
				cell = row.createCell(0)
				def strName = "${st.name}\r\n(${st.startDate ? smf.format(st.startDate) : ''}~${st.endDate ? smf.format(st.endDate) : ''})"
				cell.setCellValue(strName)

				cell = row.createCell(1)
				cell.setCellValue("版本评审")

				cell = row.createCell(2)
				strName = "${st.planReviewCompleteDate ? smf.format(st.planReviewCompleteDate): ''}"
				cell.setCellValue(strName)

				cell = row.createCell(3)
				cell.setCellValue("完成该里程碑的功能需求列表定义与计划评审")

				cell = row.createCell(4)

				for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
					if(colIx == 0 || colIx ==2)
						row.getCell(colIx).setCellStyle(redBoldStyle)
					else
						row.getCell(colIx).setCellStyle(dataStyle)
				}
				row = sheet3.createRow(rowNo++)
				row.createCell(0)
				cell = row.createCell(1)
				cell.setCellValue("测试方案评审")
				cell = row.createCell(2)
				strName = "${st.planTestplanReviewCompleteDate ? smf.format(st.planTestplanReviewCompleteDate) : ''}"
				cell.setCellValue(strName)
				cell = row.createCell(3)
				cell.setCellValue("完成该里程碑的功能需求的测试方案评审")
				cell = row.createCell(4)
				cell.setCellValue("")
				for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
					if(colIx == 0 || colIx ==2)
						row.getCell(colIx).setCellStyle(redBoldStyle)
					else
						row.getCell(colIx).setCellStyle(dataStyle)
				}

				row = sheet3.createRow(rowNo++)
				row.createCell(0)
				cell = row.createCell(1)
				cell.setCellValue("开发专项开发测试")
				cell = row.createCell(2)
				strName = "${st.planDevelopCompleteDate ? smf.format(st.planDevelopCompleteDate) : ''}"
				cell.setCellValue(strName)
				cell = row.createCell(3)
				cell.setCellValue("完成该里程碑功能的设计、开发与自测")
				cell = row.createCell(4)
				cell.setCellValue("")
				for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
					if(colIx == 0 || colIx ==2)
						row.getCell(colIx).setCellStyle(redBoldStyle)
					else
						row.getCell(colIx).setCellStyle(dataStyle)
				}
				row = sheet3.createRow(rowNo++)
				row.createCell(0)
				cell = row.createCell(1)
				cell.setCellValue("测试与开发测试")
				cell = row.createCell(2)
				strName = "${st.planTestCompleteDate ? smf.format(st.planTestCompleteDate) : ''}"
				cell.setCellValue(strName)
				cell.setCellStyle(redBoldStyle)
				cell = row.createCell(3)
				cell.setCellValue("完成该里程碑的系统测试")
				cell = row.createCell(4)
				cell.setCellValue("")
				for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
					if(colIx == 0 || colIx ==2)
						row.getCell(colIx).setCellStyle(redBoldStyle)
					else
						row.getCell(colIx).setCellStyle(dataStyle)
				}

				row = sheet3.createRow(rowNo++)
				row.createCell(0)
				cell = row.createCell(1)
				cell.setCellValue("产品管理与测试团队测试")
				cell = row.createCell(2)
				cell.setCellValue("${st.planSystemTestCompleteDate ? smf.format(st.planSystemTestCompleteDate) : ''}")
				cell = row.createCell(3)
				cell.setCellValue("完成该里程碑产品的测试")
				cell = row.createCell(4)
				cell.setCellValue("")
				for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
					if(colIx == 0 || colIx ==2)
						row.getCell(colIx).setCellStyle(redBoldStyle)
					else
						row.getCell(colIx).setCellStyle(dataStyle)
				}

				if((++index) < stones.size()){
					row = sheet3.createRow(rowNo++)
					sheet3.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 0, 4))
					cell = row.createCell(0)
					cell.setCellValue(" ")
				}
			}

			//-----------------------版本开发计划--------------------------------
			rowNo = 0
			row = sheet4.createRow(rowNo++)
			row.setHeight((short)15.625 * 30)
			sheet4.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1, 0, 6))
			cell = row.createCell(0)
			cell.setCellValue("版本开发计划")
			for(int i = 1; i < 7; i++) row.createCell(i)

			for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
				row.getCell(colIx).setCellStyle(firstLineStyle)
			}
			strHead = [
				"里程碑",
				"功能项",
				"功能项对应根任务",
				"一级子任务",
				"开始时间",
				"计划完成时间",
				"实际完成时间"
			]
			row = sheet4.createRow(rowNo++)
			for(int i = 0; i< strHead.size(); i++){
				cell = row.createCell(i)
				cell.setCellValue(strHead[i])
				cell.setCellStyle(headStyle)
				short width = (short) 35.7 * 200
				if(i == 4 || i== 5 || i== 6) width = (short) 35.7 * 120
				sheet4.setColumnWidth((short) i, width)
			}

			stones.each{st->
				int stoneTaskCnt = 0
				def stFunc = MilestoneFunction.withCriteria{
					eq("milestone", st)
					order("startDate", "asc")
				}
				if(stFunc.size() > 0){
					int funIdx = 0
					stFunc.each{ func->
						def task = Task.withCriteria{
							//关联的跟任务
							eq("milestoneFunc",func)
							isNull("parent")
						}[0]

						int taskRowCount = 0
						if(task){
							def subTask =  Task.withCriteria{
								//一级子任务
								eq("parent", task)
							}
							if(subTask.size() > 0){
								taskRowCount = subTask.size() > 1 ? subTask.size() : 1
							}else{
								taskRowCount = 1
							}
						} else{
							taskRowCount = 1
						}
						stoneTaskCnt += taskRowCount
					}
				}else{
					stoneTaskCnt = 1
				}

				row = sheet4.createRow(rowNo++)
				if(stoneTaskCnt > 1){
					sheet4.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1 + stoneTaskCnt-1, 0, 0))
				}
				cell = row.createCell(0)
				def strVal = "${st.name}\r\n(${st.startDate ? smf.format(st.startDate) : ''}~${st.endDate ? smf.format(st.endDate) : ''})"
				cell.setCellValue(strVal)

				if(stFunc.size() == 0){
					for(int j = 1; j < 7; j++) row.createCell(j)
					for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
						row.getCell(colIx).setCellStyle(dataStyle)
					}
				}
				for(int idx = 0;  idx < stFunc.size(); idx++){
					def funcTaskCnt = 0, subTaskList = [];
					def func = stFunc[idx]
					def task = Task.withCriteria{
						//关联的跟任务
						eq("milestoneFunc",func)
						isNull("parent")
					}[0]

					if(idx != 0){
						row = sheet4.createRow(rowNo++)
						row.createCell(0)
					}

					if(task){
						subTaskList = Task.withCriteria{
							eq("parent", task)
							order("startDate", "asc")
						}
						funcTaskCnt = subTaskList.size()
						if(funcTaskCnt > 0){
							for(int jdx = 0; jdx < funcTaskCnt; jdx++){
								def subTask = subTaskList[jdx]
								if(jdx != 0) {
									row = sheet4.createRow(rowNo++)
									cell = row.createCell(0)
									cell = row.createCell(1)
									cell = row.createCell(2)
								} else {
									if(funcTaskCnt > 1){
										sheet4.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1 + funcTaskCnt-1, 1, 1))
										sheet4.addMergedRegion(new CellRangeAddress(rowNo-1, rowNo-1 + funcTaskCnt-1, 2, 2))
									}
									cell = row.createCell(1)
									cell.setCellValue(func.name)
									cell = row.createCell(2)
									cell.setCellValue(task.name)
								}

								cell = row.createCell(3)
								cell.setCellValue(subTask.name)
								cell = row.createCell(4)
								strVal = subTask.startDate? smf.format(subTask.startDate):""
								cell.setCellValue(strVal)
								cell = row.createCell(5)
								strVal = subTask.planFinishDate? smf.format(subTask.planFinishDate):""
								cell.setCellValue(strVal)
								cell = row.createCell(6)
								strVal = subTask.finishDate? smf.format(subTask.finishDate):""
								cell.setCellValue(strVal)
								for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
									row.getCell(colIx).setCellStyle(dataStyle)
								}
							}
						} else {
							cell = row.createCell(1)
							cell.setCellValue(func.name)
							cell = row.createCell(2)
							cell.setCellValue(task.name)
							cell = row.createCell(3)
							cell = row.createCell(4)
							strVal = task.startDate? smf.format(task.startDate):""
							cell.setCellValue(strVal)
							cell = row.createCell(5)
							strVal = task.planFinishDate? smf.format(task.planFinishDate):""
							cell.setCellValue(strVal)
							cell = row.createCell(6)
							strVal = task.finishDate? smf.format(task.finishDate):""
							cell.setCellValue(strVal)
							for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
								row.getCell(colIx).setCellStyle(dataStyle)
							}
						}
					} else {
						cell = row.createCell(1)
						cell.setCellValue(func.name)
						for(int j = 2; j < 7; j++) row.createCell(j)
						for(short colIx=row.getFirstCellNum(); colIx<row.getLastCellNum(); colIx++) {
							row.getCell(colIx).setCellStyle(dataStyle)
						}
					}
				}
			}
			//---------------------------------------------
			wb.write(fileOut)
			return path + fileName
		} catch (IOException ex) {
			ex.printStackTrace()
			return null
		} finally {
			if (fileOut != null) {
				fileOut.close()
			}
		}
	}

	static String g_strPath = "/exportTmp"

	def export(){
		println params
		String filePath = "";
		String path = servletContext.getRealPath(g_strPath)

		filePath = exportToFile(params, path)
		def file = new File(filePath)
		if (file.isFile() && file.exists()) {

			InputStream input = null
			OutputStream output = null
			String filename = filePath.substring(filePath.lastIndexOf('/') + 1)
			filename = filename.substring(0, filename.lastIndexOf('_')) + ".xls" //去掉时间戳

			String agent = request.getHeader("USER-AGENT")
			if (agent != null && (agent.indexOf("Firefox") != -1 || agent.indexOf("Chrome") != -1)) {
				filename = new String(filename.getBytes("UTF-8"),"iso-8859-1")
			} else {
				filename = URLEncoder.encode(filename, "UTF-8")
			}


			try {
				response.setContentType('application/vnd.ms-excel')
				response.addHeader("Content-Disposition","attachment;filename=\"" + filename + "\"")
				response.setContentLength((int) file.length())
				input = new FileInputStream(filePath)
				output = response.getOutputStream()

				byte[] buffer = new byte[5 * 1024]
				int length = -1
				while ((length = input.read(buffer)) != -1) {
					output.write(buffer, 0, length)
				}
				output.flush()
			} finally {
				if (input != null) {
					input.close()
				}
				if (output != null) {
					output.close()
				}
				if(file.exists())
					file.delete()
			}
		} else {
			render " 导出文件不存在"
		}
	}

	def exportPreview(){
		def product = Product.get(params.prodId.toLong())
		def version
		if(params.versionId) version = ProductVersion.get(params.versionId.toLong())

		def versionPlan = """
            <tr><th colspan="4"><div class="summaryTitle">公共研发部网管版本发布计划表</div></th></tr>
            <tr><th>产品名称</th><th>产品版本</th><th colspan="2">配套软件 /软件名称(版本)</th></tr>
            """
            def softwares = SupportingSoftware.withCriteria{
                        eq('productVersion', version)
                }
                def sftString =""
                for(def i=0;i< softwares.size();i++){
                    sftString += softwares[i]?.softwareName+"（"  + softwares[i].softwareVersion + "）   "
                }
		versionPlan += """<tr><td>${product.name}</td><td>${version.name}</td> <td colspan="2" >${sftString}</td></tr>"""

		versionPlan += "<tr><th>开始日期</th> <th>实际完成日期</th><th>提交中试日期</th><th>版本释放日期</th></tr>"

		versionPlan += """
            <tr> <td>${version.planCompleteDate ? smf.format(version.planCompleteDate) : ' '}</td>
            <td>${version.actualCompleteDate ? smf.format(version.actualCompleteDate) : ' '}</td>
            <td>${version.pilotscaleExperimentDate ? smf.format(version.pilotscaleExperimentDate) : ' '}</td>
            <td>${version.realeseDate ? smf.format(version.realeseDate) : ' '}</td></tr>
        """
		versionPlan += """
        <tr><th colspan="4">版本发布工作计划</th></tr>
        <tr><th>工作阶段</th><th>时间计划</th><th colspan="2">详细说明</th></tr>

        """
		def stones =  Milestone.withCriteria{
			eq("pversion", version)
			order("no", "asc")
		}

		stones.each{st->
			versionPlan += "<tr><td>${st.name}</td> <td class='red-bold'>${st.startDate ? smf.format(st.startDate):''}~${st.endDate ? smf.format(st.endDate) : ''}</td>"
			versionPlan += "<td colspan='2' class='descripTd'>${st.description ? st.description.replace('\n', '<br>') : ''}</td></tr>"
		}




		versionPlan += """
        <tr><th colspan="4">版本描述</th></tr>
        <tr><td>版本功能说明</td><td colspan="3" class='descripTd'>${version.functionDesc ? version.functionDesc.replace('\n', '<br>') : '' }</td></tr>
        <tr><td>版本适用范围</td><td colspan="3" class='descripTd'>${version.scopeOfApplicationDesc ? version.scopeOfApplicationDesc.replace('\n', '<br>') : ''}</td></tr>
        """

		def versionFunc = """
            <tr><th colspan="23"><div class="summaryTitle">网管版本功能范围</div></th></tr>
            <tr><th>${product.name}</th><th colspan="22" style="text-align:left;">${version.name}</th></tr>
            <tr>
                    <th>序号</th>
                    <th>更改类型</th>
                    <th>功能大类</th>
                    <th>功能说明</th>
                    <th>里程碑</th>
                    <th>需求来源</th>
                    <th>承诺完成时间</th>
                    <th>case号</th>
                    <th>状态</th>
                    <th>来源版本</th>
                    <th>负责专项</th>
                    <th>参与专项</th>
                    <th>负责人</th>
                    <th>开发完成时间</th>
                    <th>评审级别</th>
                    <th>是否提供需求文档</th>
                    <th>是否代码审查</th>
                    <th>开发完成需提供的文档</th>
                    <th>文档是否需要评审</th>
                    <th>是否提供系统原型</th>
                    <th>是否单元测试</th>
                    <th>备注</th>
					<th>RDM 任务</th>
            </tr>
         """

		def functions = MilestoneFunction.withCriteria{
			eq("pversion", version)
                        eq("functionType",MilestoneFunctionType.USER_FUNCTION)
			order("startDate", "asc")
		}

		def orVersion = ""
		if(version.origins != null){ //??? 功能项上无此内容
			version.origins.each{ov->
				orVersion += ov.origin.name + ";"
			}
		}
		def index = 0
		for(MilestoneFunction func : functions){
			def chargerUser = MilestoneFunctionPartake.withCriteria{
				eq("milestoneFunction",func)
				eq("isMain", true)//负责
			}*.leaderOfpartake*.realName

			def chargeGroup = MilestoneFunctionPartake.withCriteria{
				eq("milestoneFunction",func)
				eq("isMain", true)//负责
			}*.groupOfPartake*.name

			def charger = chargerUser.size() > 0 ? chargerUser.join(",") : ""
			def group = chargeGroup.size() > 0 ? chargeGroup.join(",") : ""

			def partFunc = MilestoneFunctionPartake.withCriteria{
				eq("milestoneFunction", func)
				eq("isMain", false)//参与
			}*.leaderOfpartake*.realName

			def parter = partFunc.size() > 0 ? partFunc.join(",") : ""

			//---------------------------------------------------------------
			//Author: LJ, UpateDate:2013-11-7
			//---------------------------------------------------------------
			//显示需求来源和其超链接
			def requireOriginTd=""
			if(func.requirementOrigin){
				if(func.requirementOriginLink){
					requireOriginTd = "<a href='${func.requirementOriginLink}' target='_blank'>${func.requirementOrigin}</a>"
				}else{
					requireOriginTd = func.requirementOrigin
				}
			}

			//显示case 号及其超链接
			def teatCaseTd=""
			if(func.testCase){
				if(func.testCaseLink){
					teatCaseTd = "<a href='${func.testCaseLink}' target='_blank'>${func.testCase}</a>"
				}else{
					teatCaseTd = func.testCase
				}
			}
			
			//显示RDM及其超链接
			def rdmTaskTd=""
			if(func.rdmTask){
				if(func.rdmTaskLink){
					rdmTaskTd = "<a href='${func.rdmTaskLink}' target='_blank'>${func.rdmTask}</a>"
				}else{
					rdmTaskTd = func.rdmTask
				}
			}
			//---------------------------------------------------------------END

                    def desc = func.name ?:" "
                        if(func.description){
                                desc += "【" +  func.description.replace('\n', '<br>')  + "】" 
                        }
                                
                    versionFunc += """
                <tr>
                    <td>${++index}</td>
                    <td>${func.type ?: ""}</td>
                    <td>${func.bigType ?: ""}</td>
                    <td  class='descripTd'>${desc}</td>
                    <td>${func.milestone?.name ?: "\\"}</td>
                    <td>${requireOriginTd}</td>
                    <td>${func.requirementPromiseCompleteDate ? smf.format(func.requirementPromiseCompleteDate) : ""}</td>
                    <td>${teatCaseTd}</td>
                    <td>${func.status.label}</td>
                    <td>${orVersion}</td>
                    <td>${group}</td>
                    <td>${parter}</td>
                    <td>${charger}</td>
                    <td>${func.actualCompleteDate ? smf.format(func.actualCompleteDate ): ""}</td>
                    <td>${func.reviewLevel ?: ""}</td>
                    <td>${func.needSupportRequirementDoc ? "是":"否"}</td>
                    <td>${func.needCheckCode  ? "是":"否"}</td>
                    <td>${func.supportDesignDocType ?: ""}</td>
                    <td>${func.needReviewDesignDoc ? "是":"否"}</td>
                    <td>${func.needSupportPrototype ? "是":"否"}</td>
                    <td>${func.needUnitTesting ? "是":"否"}</td>
                    <td></td>
					<td>${rdmTaskTd}</td>
                    </tr>
            """
		}

		def milstone = """
            <tr><th colspan="5"><div class="summaryTitle">版本里程碑</div></th></tr>
            <tr><th>里程碑名称</th><th>内容</th><th>截止日期</th><th>详细说明</th><th>备注</th></tr>
            """
		index = 0
		stones.each{st->
			milstone += "<tr><td rowspan='5' class='red-bold'> ${st.name}<br>(${st.startDate ? smf.format(st.startDate) : ''}~${st.endDate ? smf.format(st.endDate) : ''}) </td>"
			milstone += "<td>版本评审</td><td class='red-bold'> ${st.planReviewCompleteDate ? smf.format(st.planReviewCompleteDate): ''}</td> <td>完成该里程碑的功能需求列表定义与计划评审</td>  <td></td></tr>"
			milstone += "<tr><td>测试方案评审</td><td class='red-bold'> ${st.planTestplanReviewCompleteDate ? smf.format(st.planTestplanReviewCompleteDate) : ''}</td> <td>完成该里程碑的功能需求的测试方案评审</td> <td></td></tr>"
			milstone += "<tr><td>开发专项开发测试</td><td class='red-bold'> ${st.planDevelopCompleteDate ? smf.format(st.planDevelopCompleteDate) : ''}</td> <td>完成该里程碑功能的设计、开发与自测</td> <td></td></tr>"
			milstone += "<tr><td>测试与开发测试</td><td class='red-bold'> ${st.planTestCompleteDate ? smf.format(st.planTestCompleteDate) : ''}</td> <td>完成该里程碑的系统测试</td> <td></td></tr>"
			milstone += "<tr><td>产品管理与测试团队测试</td><td class='red-bold'> ${st.planSystemTestCompleteDate ? smf.format(st.planSystemTestCompleteDate) : ''}</td> <td>完成该里程碑产品的测试</td> <td></td></tr>"
			index ++
                        if(index < stones.size() ){
				milstone +="<tr><td clospan='5' class='sep-tr'>&nbsp;</td></tr>"
			}
		}

		//版本开发计划
		def functionPlan = """
<tr><th colspan="7"><div class="summaryTitle">版本开发计划</div></th></tr>
<tr><th>里程碑</th><th>功能项</th><th>功能项对应根任务</th><th>一级子任务</th><th>开始时间</th><th>计划完成时间</th><th>完成时间</th></tr>
"""
		stones.each{st->
			int stoneTaskCnt = 0
			def stFunc = MilestoneFunction.withCriteria{
				eq("milestone", st)
				order("startDate", "asc")
			}
			def stoneSubRow = ""
			if(stFunc.size() > 0){
				int funIdx = 0
				stFunc.each{ func->
					def task = Task.withCriteria{
						//关联的跟任务
						eq("milestoneFunc",func)
						isNull("parent")
					}[0]

					def strSubTask = ""
					int taskRowCount = 0

					if(task){
						def subTask =  Task.withCriteria{
							//一级子任务
							eq("parent", task)
							order("startDate", "asc")
						}
						if(subTask.size() > 0){
							taskRowCount = subTask.size() > 1 ? subTask.size() : 1
							for(int i = 0; i <  subTask.size(); i ++){
								def tst = subTask[i]
								def str = """<td>${tst.name}</td><td>${tst.startDate? smf.format(tst.startDate):""}</td><td>${tst.planFinishDate? smf.format(tst.planFinishDate):""}</td><td>${tst.finishDate?smf.format(tst.finishDate):""}</td></tr>"""
								if(i > 0) str = "<tr>" + str
								strSubTask += str
							}
						}else{
							taskRowCount = 1
							strSubTask = """<td></td><td>${task.startDate? smf.format(task.startDate):""}</td><td>${task.planFinishDate? smf.format(task.planFinishDate):""}</td><td>${task.finishDate? smf.format(task.finishDate):""}</td></tr>"""
						}
					} else{
						taskRowCount = 1
						strSubTask = "<td></td><td></td><td></td><td></td></tr>"
					}

					def rs = (taskRowCount > 1) ? "rowspan='${taskRowCount}'" : ""

					def strFunc = "<td ${rs}>${func.name} </td><td ${rs}>${task?.name ?: ""} </td>" + strSubTask
					if(funIdx > 0) strFunc = "<tr>" + strFunc
					stoneSubRow += strFunc
					stoneTaskCnt += taskRowCount

					funIdx++
				}
			}else{
				stoneTaskCnt = 1
				stoneSubRow += "<td></td><td></td><td></td><td></td><td></td><td></td></tr>"
			}
			def rs = (stoneTaskCnt > 1) ? "rowspan='${stoneTaskCnt}'" : ""
			def stoneTr = "<tr><td ${rs}>${st.name}<br>(${st.startDate ? smf.format(st.startDate) : ''}~${st.endDate ? smf.format(st.endDate) : ''})</td>"
			stoneTr += stoneSubRow
			functionPlan += stoneTr
		}
		render(contentType: "text/json"){
			[versionPlan: versionPlan, versionFunc: versionFunc,
						milstone: milstone, functionPlan: functionPlan]
		}
	}

	def productConfig(){}

	def listProductData(){
		def products = Product.findAll()

		//每次显示专项任务状态定制表时, 补足没有的
		def productConfList = []

		products.each{ prd->
			def cfg = ProductConfig.withCriteria{eq("product", prd)}[0]
			if(!cfg){
				cfg = new ProductConfig()
				cfg.product = prd
				cfg.longTaskThreshold = 10
				cfg.longTaskWarningDay = 3
				cfg.needWarning = false
				cfg.warningDay = 0
				cfg.save()
				println cfg.getErrors()
			}
			productConfList <<  [id: cfg.id,
						name: prd.name,
						prodId: prd.id,
						description: prd.description,
						longTaskThreshold: cfg.longTaskThreshold,
						longTaskWarningDay: cfg.longTaskWarningDay,
						needWarning: cfg.needWarning,
						warningDay: cfg.warningDay,
						lastUpdatedBy: cfg.audited.lastUpdatedBy ?:"",
						lastUpdatedDate: cfg.audited.lastUpdatedDate ? smf.format(cfg.audited.lastUpdatedDate): ""]
		}


		render(contentType:"text/json"){
			[total:productConfList.size(), cfgList:productConfList]
		}
	}

	def updateProductConfig(){
		def gridParam = Eval.me(params.gridParam)

		def models = gridParam.models

		models.each{key, val->
			def cfg = ProductConfig.get(val.id)
			cfg.longTaskThreshold = val.longTaskThreshold
			cfg.longTaskWarningDay = val.longTaskWarningDay
			cfg.needWarning = val.needWarning
			cfg.warningDay = val.warningDay
			cfg.save()
		}

		render 'ok'
	}

	//管理关注人
	def selectUser(){

		def groups = UserGroup.withCriteria{ order("name", "asc") }
		def groupOptions = ""
		groups.each{g->
			groupOptions += "<option value=\""+g.id+"\">"+g.name+"</option>"
		}

		def mailtoUsers = []
		Product product = Product.get(params.groupId?.toLong())
		if(product){
			mailtoUsers = ProdAttentiontor.withCriteria{ eq("product", product) }*.user
		}
		println product
		println mailtoUsers

		[groupId: params.groupId, groupOptions: groupOptions, mailtoUsers: mailtoUsers]
	}

	def saveProductAttent(){
		println "saveProductAttent : " + params
		def mailToIds = []
		if(params?.ids && params?.ids != ''){

			params?.ids?.split(',').each{
				mailToIds << it.toLong()
			}
			Product product = Product.get(params.groupId?.toLong())
			mailToIds.each{ uId->
				def u = User.get(uId)
				if(u){
					def uList = ProdAttentiontor.withCriteria {
						eq('user', u)
						eq('product', product)
					}
					//如果没有查找关注人，就增加
					if(uList?.size() == 0){
						def aa = new ProdAttentiontor()
						aa.product = product
						aa.user = u
						aa.save(flush:true)
					}
				}
			}
			render 'ok'
		} else { //删除所有(全不选)
			Product product = Product.get(params.groupId?.toLong())
			def uList = ProdAttentiontor.withCriteria { eq('product', product) }
			uList.each{ it.delete() }
			render 'ok'
		}
	}

	//删除
	def deleteAttentiontor(){
		def u = User.get(params?.id)
		Product product = Product.get(params.groupId?.toLong())
		if(u){
			def uList = ProdAttentiontor.withCriteria {
				eq('user', u)
				eq('product', product)
			}
			//
			uList.each{
				it.delete(flush:true)
			}
		}
		render 'ok'
	}

	//异步设置关注
	def ajaxSetAttent(){
		println "========= ajaxSetAttent: " + params
		def res
		if(params?.id){
			ProductGuidepost route = ProductGuidepost.get(params.id.toLong())
			if(route){
				User user = session.user

				def existAtt = AttentRoute.findByRouteAndUser(route, user)
				if(existAtt){
					existAtt.delete()
					res = [stat: "ok", type: "D", msg: "取消关注成功。"]
				} else {
					AttentRoute att = new AttentRoute()
					att.route = route
					att.user = user
					if(!att.save(flush: true)){
						res = [stat: "fail", msg: "设置关注错误: " + att.getErrors()]
					}else{
						res = [stat: "ok", type: "S", msg: "设置关注成功。"]
					}
				}
			}else{
				res = [stat: "fail", msg: "路标不存在或已被删除。"]
			}
		}
		render res as JSON
	}
}
